package com.example.demo.game;

/**
 * Created with IntelliJ IEDA.
 * Description:
 * User:86186
 * Date:2023-10-28
 * Time:15:52
 */

import com.example.demo.entity.Userinfo;
import com.example.demo.response.MatchResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Queue;

/**
 * 这个类表示“匹配器”，通过这个类负责完成整个匹配功能
 */
@Component
public class Matcher {
    // 创建三个匹配队列
    private Queue<Userinfo> normalQueue = new ArrayDeque<>();
    private Queue<Userinfo> highQueue = new ArrayDeque<>();
    private Queue<Userinfo> veryhighQueue = new ArrayDeque<>();

    @Autowired
    private OnlineUserManager onlineUserManager;

    @Autowired
    private RoomManager roomManager;

    private ObjectMapper objectMapper = new ObjectMapper();


    // 操作匹配队列的方法.
    // 把玩家放到匹配队列中
    public void add(Userinfo user) {
        if (user.getScore() < 2000) {
            synchronized (normalQueue) {
                normalQueue.offer(user);
                normalQueue.notify();
            }
            System.out.println("把玩家 " + user.getUsername() + " 加入到了 normalQueue 中!");
        } else if (user.getScore() >= 2000 && user.getScore() < 3000) {
            synchronized (highQueue) {
                highQueue.offer(user);
                highQueue.notify();
            }
            System.out.println("把玩家 " + user.getUsername() + " 加入到了 highQueue 中!");
        } else {
            synchronized (veryhighQueue) {
                veryhighQueue.offer(user);
                veryhighQueue.notify();
            }
            System.out.println("把玩家 " + user.getUsername() + " 加入到了 veryHighQueue 中!");
        }
    }


    // 当玩家点击停止匹配的时候, 就需要把玩家从匹配队列中删除
    public void remove(Userinfo user) {
        if (user.getScore() < 2000) {
            synchronized (normalQueue) {
                normalQueue.remove(user);
            }
            System.out.println("把玩家 " + user.getUsername() + " 移除了 normalQueue!");
        } else if (user.getScore() >= 2000 && user.getScore() < 3000) {
            synchronized (highQueue) {
                highQueue.remove(user);
            }
            System.out.println("把玩家 " + user.getUsername() + " 移除了 highQueue!");
        } else {
            synchronized (veryhighQueue) {
                veryhighQueue.remove(user);
            }
            System.out.println("把玩家 " + user.getUsername() + " 移除了 veryHighQueue!");
        }
    }


    public Matcher() {
        // 创建三个线程, 分别针对这三个匹配队列, 进行操作.
        Thread t1 = new Thread() {
            @Override
            public void run() {
                // 扫描 normalQueue
                while (true) {
                    handlerMatch(normalQueue);
                }
            }
        };
        t1.start();

        Thread t2 = new Thread(){
            @Override
            public void run() {
                while (true) {
                    handlerMatch(highQueue);
                }
            }
        };
        t2.start();

        Thread t3 = new Thread() {
            @Override
            public void run() {
                while (true) {
                    handlerMatch(veryhighQueue);
                }
            }
        };
        t3.start();
    }


    private void handlerMatch(Queue<Userinfo> matchQueue) {
        synchronized (matchQueue) {
            try {
                // 1.检测队列中玩家个数
                // 队列的初始情况可能为空
                // 如果往队列中天界一个元素，这个时候，仍然是不能进行后续操作的
                // 因此在这里使用 while 循环检查是更合理的
                while (matchQueue.size() < 2) {
                    matchQueue.wait();
                }
                // 2. 尝试从队列中取出两个玩家
                Userinfo userinfo1 = matchQueue.poll();
                Userinfo userinfo2 = matchQueue.poll();
                System.out.println("匹配出两个玩家: " + userinfo1.getUsername() + ", " + userinfo2.getUsername());
                // 3. 获取到玩家的 websocket 的会话
                //    获取到会话的目的是为了告诉玩家, 你排到了~~
                WebSocketSession session1 = onlineUserManager.getFromGameHall(userinfo1.getId());
                WebSocketSession session2 = onlineUserManager.getFromGameHall(userinfo2.getId());

                // 理论上来叔，匹配队列中的玩家一定是在线的状态
                // 因为前面的逻辑里进行了处理，当玩家断开连接的时候就把玩家从匹配队列中移除了
                // 但是此处仍然进行一次判定，更加稳健
                if (session1 == null) {
                    // 如果玩家1 现在不在线了, 就把玩家2 重新放回到匹配队列中
                    matchQueue.offer(userinfo2);
                    return;
                }
                if (session2 == null) {
                    // 如果玩家2 现在下线了, 就把玩家1 重新放回匹配队列中
                    matchQueue.offer(userinfo1);
                    return;
                }
                // 当前能否排到玩家是同一个用户的情况呢？一个玩家入队列两次
                // 理论上也不会存在
                // 1)如果玩家下线，就会对玩家移除匹配队列
                // 2)又禁止了玩家多开
                // 但是此处仍然进行一次判定，更加稳健
                if (session1 == session2) {
                    // 把其中的一个玩家放回匹配队列.
                    matchQueue.offer(userinfo1);
                    return;
                }

                // 4.把两个玩家放到一个游戏房间中
                Room room = new Room();
                roomManager.add(room, userinfo1.getId(), userinfo1.getId());


                // 5.给玩家反馈信息：你匹配到对手了
                //   通过 webSocket 返回一个 messsage 为 "matchSuccess" 这样的响应
                MatchResponse response1 = new MatchResponse();
                response1.setOk(true);
                response1.setMessage("matchSuccess");
                String json1 = objectMapper.writeValueAsString(response1);
                session1.sendMessage(new TextMessage(json1));

                MatchResponse response2 = new MatchResponse();
                response2.setOk(true);
                response2.setMessage("matchSuccess");
                String json2 = objectMapper.writeValueAsString(response2);
                session2.sendMessage(new TextMessage(json2));
            }catch (IOException | InterruptedException e){
                e.printStackTrace();
            }
        }

    }
}
